﻿using GylCSharpUtils.Common.Utils;
using System;
using System.Collections.Generic;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace GylCSharpUtils.P2PIPC.Utils {
    /// <summary>
    /// 无缓冲共享内存，两个方向都只能写入一条消息
    /// </summary>
    public class NoBufferShareMemory {

        public struct BaseInfo {
            public long match_flag; // 匹配标志，只有拥有相同标志的两个对象才可互相读写
            public long timeoutOfMillSeconds;    // 超时时间
            public UserInfo aInfo;  // 用户A的信息
            public UserInfo bInfo;  // 用户B的信息
            public MsgInfo msgAInfo;    // 用户A需要读取的消息
            public MsgInfo msgBInfo;    // 用户B需要读取的消息

        }
        public struct UserInfo {
            public long Id;    // 用户A的Id
            public long LastOperateTime;  // 用户A操作的最后时间
        }
        public struct MsgInfo {
            public bool CanRead;   // 用户A是否可读,用户A不可读时用户B才可写
            public int ReadLen;    // A需要读取的长度
        }

        private static readonly long MATCH_FLAG = 2436850793510140931L;
        private static readonly int FLAG_SIZE = sizeof(long);
        private static readonly int TIMEOUT_SIZE = sizeof(long);
        private static readonly int USER_INFO_SIZE = Marshal.SizeOf<UserInfo>();
        private static readonly int MSG_INFO_SIZE = Marshal.SizeOf<MsgInfo>();
        private static readonly int BASE_INFO_SIZE = Marshal.SizeOf<BaseInfo>();

        // 用户A的信息的位置
        private static readonly int USER_A_INFO_POSITION = FLAG_SIZE + TIMEOUT_SIZE;
        // 用户B的信息的位置
        private static readonly int USER_B_INFO_POSITION = USER_A_INFO_POSITION + USER_INFO_SIZE;
        private static readonly int MSG_A_INFO_POSITION = USER_B_INFO_POSITION + USER_INFO_SIZE;
        private static readonly int MSG_B_INFO_POSITION = MSG_A_INFO_POSITION + MSG_INFO_SIZE;

        public int MAX_MSG_SIZE { get; private set; } = 1 << 22; // 最大消息大小，默认4M
        private int MSG_A_POSITION;
        private int MSG_B_POSITION;

        private readonly long timeoutOfMillSeconds;
        private long UserID;
        private bool IsUserA;   // 是不是A用户
        public string RealPath { get; private set; }
        private MemoryMappedFile Mmf;
        private MemoryMappedViewAccessor BaseInfoView;
        private MemoryMappedViewAccessor MsgInfoAView;
        private MemoryMappedViewAccessor MsgInfoBView;
        private MemoryMappedViewAccessor MsgAView;
        private MemoryMappedViewAccessor MsgBView;

        /// <summary>
        /// 创建无缓冲的共享内存
        /// </summary>
        /// <param name="path">共享内存的唯一标识</param>
        /// <param name="maxLenPerMsg">最大消息长度</param>
        /// <param name="isUserA">使用A用户</param>
        /// <param name="timeoutOfMillSeconds">最大无响应时间（毫秒），默认60s</param>
        public NoBufferShareMemory(string path, int maxLenPerMsg, bool isUserA, long timeoutOfMillSeconds = 60 * 1000) {
            if(string.IsNullOrEmpty(path)) {
                throw new ArgumentException("Path 不可为空");
            }
            UserID = RandomUtils.RandomLong();
            // 合成最后的路径
            path = string.Format("{0}.{1}", GetType().FullName, path);
            RealPath = path;
            // 计算需要的共享内存文件的大小
            this.timeoutOfMillSeconds = timeoutOfMillSeconds;
            MAX_MSG_SIZE = maxLenPerMsg;
            MSG_A_POSITION = BASE_INFO_SIZE;
            MSG_B_POSITION = MSG_A_POSITION + MAX_MSG_SIZE;
            int mmfSize = MSG_B_POSITION + MAX_MSG_SIZE;
            Mmf = MemoryMappedFile.CreateOrOpen(path, mmfSize);
            // 初始化一下BaseInfoView
            ReadBaseInfo(out BaseInfo baseInfo);
            CheckMmfType(); // 检查共享内存的类型
            timeoutOfMillSeconds = baseInfo.timeoutOfMillSeconds;   // 使用已经设置好的超时时间
            this.IsUserA = isUserA;
            // 尝试获取用户
            TryRegCurrentUser();
            // 用户注册成功
        }


        #region 类型信息相关

        /// <summary>
        /// 检查共享内存的类型是否是该类需要的类型，若不是则直接报错
        /// </summary>
        private void CheckMmfType() {
            long flag = ReadMatchFlag();
            if(flag == 0) {
                WriteMatchFlag();
                CheckMmfType(); // 重新检查一遍
            } else if(flag == MATCH_FLAG) {
                return;
            } else {
                throw new Exception("存在同名但使用类型不匹配的共享内存");
            }
        }

        /// <summary>
        /// 读取类型匹配信息
        /// </summary>
        /// <returns></returns>
        private long ReadMatchFlag() {
            return BaseInfoView.ReadInt64(0);
        }
        /// <summary>
        /// 写入类型匹配标志
        /// </summary>
        private void WriteMatchFlag() {
            BaseInfoView.Write(0, MATCH_FLAG);
            BaseInfoView.Write(FLAG_SIZE, timeoutOfMillSeconds);
        }
        #endregion



        #region 用户相关
        /// <summary>
        /// 尝试注册当前用户，注册失败直接报错
        /// </summary>
        /// <param name="maxTryMillSecond">最大尝试时间(ms), 默认5s</param>
        /// <returns>是否未用户A</returns>
        private void TryRegCurrentUser(int maxTryMillSecond = 5 * 1000) {
            // 首先判断用户A是否存活
            if(UserAlive()) { // 目标位置的用户存活，
                throw new Exception(string.Format("用户{0}正在活动，无法重复添加用户", IsUserA ? "A" : "B"));
            }
            int ms = DateTime.Now.Millisecond;
            int ct;
            for(int i = 0; true; i++) {
                ct = DateTime.Now.Millisecond - ms;
                if(ct > maxTryMillSecond) {
                    throw new Exception(string.Format("注册用户尝试{0}次，用时{1}ms,已超时", i, ct));
                }
                if(WriteUserInfo().Id == UserID) {  // 写入成功，进行验证
                    return;
                }
                Thread.Sleep(RandomUtils.RandomInt(1, 10));
            }

        }

        /// <summary>
        /// 判断用户是否存活
        /// </summary>
        /// <param name="userA">true代表判断用户A</param>
        /// <returns></returns>
        public bool UserAlive() {
            var info = ReadUserInfo();
            if(info.Id == 0) return false;
            if(GetNowTime() - info.LastOperateTime >= timeoutOfMillSeconds) return false;
            return true;
        }

        /// <summary>
        /// 读取用户信息
        /// </summary>
        /// 
        public UserInfo ReadUserInfo() {
            BaseInfoView.Read(IsUserA ? USER_A_INFO_POSITION : USER_B_INFO_POSITION, out UserInfo userInfo);
            return userInfo;
        }
        /// <summary>
        /// 写入用户信息
        /// </summary>
        /// <returns></returns>
        public UserInfo WriteUserInfo() {
            var info = new UserInfo {
                Id = UserID,
                LastOperateTime = GetNowTime(),
            };
            BaseInfoView.Write(IsUserA ? USER_A_INFO_POSITION : USER_B_INFO_POSITION, ref info);
            return info;
        }
        /// <summary>
        /// 获取总ms数
        /// </summary>
        /// <returns></returns>
        public long GetNowTime() {
            return TimeUtils.GetAllMillSeconds();
        }

        #endregion


        /// <summary>
        /// 读取基本信息
        /// </summary>
        /// <param name="info">基本信息</param>
        public void ReadBaseInfo(out BaseInfo info) {
            if(BaseInfoView == null) {
                BaseInfoView = Mmf.CreateViewAccessor(0, BASE_INFO_SIZE);
            }
            BaseInfoView.Read(0, out info);
        }

        #region 消息相关

        public bool CanWrite() {
            return CanWrite(out BaseInfo info);
        }
        public bool CanWrite(out BaseInfo info) {
            ReadBaseInfo(out info);
            bool flag = true;
            if(IsUserA) {
                // B端可以读时用户A就不能写入
                if(info.msgBInfo.CanRead) flag = false;
            } else {
                // A端可以读时用户B就不能写入
                if(info.msgAInfo.CanRead) flag = false;
            }
            WriteUserInfo();
            return flag;
            ;
        }

        /// <summary>
        /// 写数据
        /// </summary>
        /// <param name="data">数据</param>
        /// <returns>是否成功</returns>
        public bool WriteData(byte[] data) {
            if(data == null || data.Length == 0) return false;
            if(!CanWrite(out BaseInfo info)) return false;
            int len = data.Length;
            // 需要向另一个用户的端写入数据
            MemoryMappedViewAccessor view = IsUserA ? GetMsgBView() : GetMsgAView();
            view.WriteArray(0, data, 0, len); // 数据写入完成
            // 写入另一端可读
            SetCanRead(len);

            return true;
        }
        /// <summary>
        /// 设置另一端可读
        /// </summary>
        /// <param name="len">数据长度</param>
        private void SetCanRead(int len) {
            MemoryMappedViewAccessor view = IsUserA ? GetMsgInfoBView() : GetMsgInfoAView();
            var info = new MsgInfo {
                CanRead = true,
                ReadLen = len
            };
            view.Write(0, ref info);
        }

        private void SetCanWrite() {
            // 可向当前端写入
            MemoryMappedViewAccessor view = IsUserA ? GetMsgInfoAView() : GetMsgInfoBView();
            var info = new MsgInfo {
                CanRead = false,
                ReadLen = 0
            };
            view.Write(0, ref info);
        }


        public bool CanRead() {
            return CanRead(out BaseInfo info);
        }
        public bool CanRead(out BaseInfo info) {
            ReadBaseInfo(out info);
            bool flag = false;
            if(IsUserA) {
                // A端可读时用户A可读
                if(info.msgAInfo.CanRead) flag = true;
            } else {
                // B端可读时用户B可读
                if(info.msgBInfo.CanRead) flag = true;
            }
            WriteUserInfo();
            return flag;

        }
        public byte[] ReadData() {
            if(!CanRead(out BaseInfo info)) return null;
            MemoryMappedViewAccessor view = IsUserA ? GetMsgAView() : GetMsgBView();
            int len = IsUserA ? info.msgAInfo.ReadLen : info.msgBInfo.ReadLen;
            byte[] data = new byte[len];
            view.ReadArray(0, data, 0, len);
            SetCanWrite();
            return data;
        }



        private MemoryMappedViewAccessor GetMsgInfoAView() {
            if(MsgInfoAView == null) {
                MsgInfoAView = Mmf.CreateViewAccessor(MSG_A_INFO_POSITION, MSG_INFO_SIZE);
            }
            return MsgInfoAView;
        }
        private MemoryMappedViewAccessor GetMsgInfoBView() {
            if(MsgInfoBView == null) {
                MsgInfoBView = Mmf.CreateViewAccessor(MSG_B_INFO_POSITION, MSG_INFO_SIZE);
            }
            return MsgInfoBView;
        }
        private MemoryMappedViewAccessor GetMsgAView() {
            if(MsgAView == null) {
                MsgAView = Mmf.CreateViewAccessor(MSG_A_POSITION, MAX_MSG_SIZE);
            }
            return MsgAView;
        }
        private MemoryMappedViewAccessor GetMsgBView() {
            if(MsgBView == null) {
                MsgBView = Mmf.CreateViewAccessor(MSG_B_POSITION, MAX_MSG_SIZE);
            }
            return MsgBView;
        }


        #endregion
    }
}
